package com.jieli.bt.att.tool.scan

import android.annotation.SuppressLint
import android.bluetooth.BluetoothAdapter
import android.bluetooth.BluetoothDevice
import android.bluetooth.BluetoothManager
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.os.Handler
import android.os.Looper
import com.jieli.bt.att.MyApplication
import com.jieli.bt.att.data.constant.Config
import com.jieli.bt.att.tool.ble.interfaces.IBtScanCallback
import com.jieli.bt.att.tool.ble.model.ScanDeviceInfo
import com.jieli.bt.att.util.BluetoothUtil
import com.jieli.bt.att.util.PermissionUtil
import com.jieli.logcat.JL_Log

/**
 * BtScanner
 * @author zqjasonZhong
 * @since 2025/3/27
 * @email zhongzhuocheng@zh-jieli.com
 * @desc 蓝牙扫描器
 */
@SuppressLint("MissingPermission")
class BtScanner private constructor(private val context: Context) {

    companion object {

        private const val MSG_SCAN_DEVICE_TIMEOUT = 0x4000

        @SuppressLint("StaticFieldLeak")
        @Volatile
        private var instance: BtScanner? = null

        fun getInstance(): BtScanner {
            if (null == instance) {
                synchronized(BtScanner::class.java) {
                    if (null == instance) {
                        instance = BtScanner(MyApplication.application)
                    }
                }
            }
            return instance!!
        }

    }

    private val TAG = BtScanner::class.java.simpleName

    private val btAdapter: BluetoothAdapter

    var isScanning = false
        private set

    private var btReceiver: BluetoothScanReceiver? = null

    private var callback: IBtScanCallback? = null

    private val uiHandler = Handler(Looper.getMainLooper()) { msg ->
        if (MSG_SCAN_DEVICE_TIMEOUT == msg.what) {
            stopScan()
        }
        true
    }

    init {
        val btManager = context.getSystemService(Context.BLUETOOTH_SERVICE) as BluetoothManager?
        val adapter: BluetoothAdapter? = btManager?.adapter
        btAdapter = adapter ?: BluetoothAdapter.getDefaultAdapter()
        if (PermissionUtil.hasBluetoothScanPermission(context)) {
            isScanning = btAdapter.isEnabled && btAdapter.isDiscovering
        }
    }

    fun startScan(timeout: Long, callback: IBtScanCallback) {
        if (isScanning) {
            callback.onDiscoveryFail(0x0100, "It is scanning.")
            return
        }
        isScanning = true
        this.callback = callback
        registerReceiver()
        uiHandler.removeMessages(MSG_SCAN_DEVICE_TIMEOUT)
        if (timeout > 0) {
            uiHandler.sendEmptyMessageDelayed(MSG_SCAN_DEVICE_TIMEOUT, timeout)
        }
        postDiscoveryState(true)
        if (!btAdapter.startDiscovery()) {
            callback.onDiscoveryFail(0x0101, "Failed to scan device.")
            stopScan()
        }
    }

    fun stopScan() {
        if (!isScanning) return
        isScanning = false
        uiHandler.removeMessages(MSG_SCAN_DEVICE_TIMEOUT)
        if (btAdapter.isEnabled && btAdapter.isDiscovering) {
            btAdapter.cancelDiscovery()
        }
        unregisterReceiver()
        postDiscoveryState(false)
    }

    fun destroy() {
        stopScan()
        instance = null
    }

    private fun postBluetoothAdapterChange(isOpen: Boolean) {
        callback?.onAdapterChange(isOpen)
        if (isScanning) {
            stopScan()
        }
    }

    private fun postDiscoveryState(isStart: Boolean) {
        callback?.onDiscoveryState(isStart)
        if (isStart) {
            JL_Log.d(TAG, "postDiscoveryState", "start... ")
            BluetoothUtil.getSystemConnectedBtDeviceList(context)?.let { devices ->
                JL_Log.d(
                    TAG,
                    "postDiscoveryState",
                    "getSystemConnectedBtDeviceList : " + devices.size
                )
                for (device in devices) {
                    if (device.type != BluetoothDevice.DEVICE_TYPE_LE) {
                        JL_Log.d(
                            TAG,
                            "postDiscoveryState",
                            "device : " + BluetoothUtil.printBtDeviceInfo(context, device)
                        )
                        postFoundDevice(
                            ScanDeviceInfo(
                                device
                            ).setRssi(0).setEnableConnect(true)
                        )
                    }
                }
            }
        } else {
            callback = null
        }
    }

    private fun postFoundDevice(scanInfo: ScanDeviceInfo) {
        if (Config.IS_FILTER_NO_NAME_DEVICE) {
            val name = BluetoothUtil.getDeviceName(scanInfo.device, false)
            if ("N/A" == name) return
        }
        callback?.onDiscoveryDevice(scanInfo)
    }

    private fun registerReceiver() {
        if (null == btReceiver) {
            BluetoothScanReceiver().let {
                btReceiver = it
                context.registerReceiver(btReceiver, IntentFilter().apply {
                    addAction(BluetoothAdapter.ACTION_STATE_CHANGED)
                    addAction(BluetoothAdapter.ACTION_DISCOVERY_STARTED)
                    addAction(BluetoothAdapter.ACTION_DISCOVERY_FINISHED)
                    addAction(BluetoothDevice.ACTION_FOUND)
                })
            }
        }
    }

    private fun unregisterReceiver() {
        btReceiver?.let {
            context.unregisterReceiver(it)
        }
        btReceiver = null
    }

    inner class BluetoothScanReceiver : BroadcastReceiver() {

        override fun onReceive(context: Context?, intent: Intent?) {
            intent?.action?.let { action ->
                when (action) {
                    BluetoothAdapter.ACTION_STATE_CHANGED -> {
                        val state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, -1)
                        val prevState =
                            intent.getIntExtra(BluetoothAdapter.EXTRA_PREVIOUS_STATE, -1)
                        JL_Log.d(
                            TAG,
                            "ACTION_STATE_CHANGED",
                            "prevState : $prevState, state : $state"
                        )
                        if (state == -1 || prevState == state) return
                        if (state == BluetoothAdapter.STATE_OFF) { //蓝牙关闭
                            postBluetoothAdapterChange(false)
                        } else if (state == BluetoothAdapter.STATE_ON) { //蓝牙打开
                            postBluetoothAdapterChange(true)
                        }
                    }

                    BluetoothAdapter.ACTION_DISCOVERY_STARTED -> {
                        JL_Log.d(TAG, "ACTION_DISCOVERY_STARTED", "--->")
                    }

                    BluetoothAdapter.ACTION_DISCOVERY_FINISHED -> {
                        JL_Log.d(TAG, "ACTION_DISCOVERY_FINISHED", "--->")
                        stopScan()
                    }

                    BluetoothDevice.ACTION_FOUND -> {
                        if (!isScanning) return
                        val device =
                            intent.getParcelableExtra<BluetoothDevice?>(BluetoothDevice.EXTRA_DEVICE)
                                ?: return
                        if (device.type == BluetoothDevice.DEVICE_TYPE_LE) return
                        val rssi = intent.getShortExtra(BluetoothDevice.EXTRA_RSSI, 0)
                        postFoundDevice(
                            ScanDeviceInfo(
                                device
                            ).setRssi(rssi.toInt()).setEnableConnect(true)
                        )
                    }
                }
            }
        }

    }

}